package org.desktop.intbox.business.categories.impl;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.desktop.intbox.business.categories.IFileCategory;
import org.desktop.intbox.business.categories.IFileCategoryEntity;

public class FileCategory extends FileCategoryEntity implements IFileCategory {

	private static final int MAX_CACHE_SIZE = 200;

	private final Map<String, FileCategoryEntity> childrenByName = new LinkedHashMap<>();
	private final Map<String, FileCategoryEntity> unmodifiableChildrenByName = Collections
			.unmodifiableMap(childrenByName);

	private final Map<String, IFileCategoryEntity> cache = new HashMap<>();

	public FileCategory() {
		super();
	}

	public FileCategory(final Integer color, final String name) {
		super(color, name);
	}

	public FileCategory(final Integer color) {
		super(color);
	}

	public FileCategory(final String name) {
		super(name);
	}

	@Override
	public Collection<FileCategoryEntity> getChildren() {
		return unmodifiableChildrenByName.values();
	}

	public void addChild(final FileCategoryEntity entry) {
		childrenByName.put(entry.getName(), (entry));
		cache.clear();
		if (entry.getParent() != this) {
			entry.setParent(this);
		}
	}

	@Override
	public IFileCategoryEntity getEntryFor(final String categoryName, final double depthRatio) {
		IFileCategoryEntity exactMatch = getEntryFor(categoryName);
		IFileCategoryEntity entity = exactMatch;
		List<IFileCategoryEntity> parents = new LinkedList<>();
		while (entity != null) {
			parents.add(0, entity);
			entity = entity.getParent();
		}
		int idx = (int) Math.round(parents.size() * depthRatio);
		idx = idx <= 0 ? 0 : idx - 1;

		return depthRatio >= 1 ? exactMatch : parents.get(idx);
	}

	@Override
	public IFileCategoryEntity getEntryFor(final String categoryName) {
		String normalizedExtension = normalizeName(categoryName);
		IFileCategoryEntity result = null;
		if (cache.containsKey(normalizedExtension)) {
			result = cache.get(normalizedExtension);
		} else {

			if (normalizeName(getName()).equals(normalizedExtension)) {
				result = this;
			} else

			// Try to find the extension directly within this category.
			// That should be more efficient since we expect a "wide" tree.
			if (childrenByName.containsKey(normalizedExtension)) {
				result = childrenByName.get(normalizedExtension).getEntryFor(normalizedExtension);
			} else {
				for (Map.Entry<String, FileCategoryEntity> childEntry : childrenByName.entrySet()) {
					result = childEntry.getValue().getEntryFor(categoryName);
					if (result != null) {
						break;
					}
				}
			}
			if (cache.size() >= MAX_CACHE_SIZE) {
				cache.clear();
			}
			cache.put(normalizedExtension, result);
		}

		// If this is the root directory & we can't find anything for
		// this extensions - then use the settings for the
		// root category as a fall back.
		return result == null && getParent() == null ? this : result;
	}

}
